(function($){
    
    var IS_FIRST_STATE_CLASS = "isFirstState";
    var FIRST_STATE_IMAGE = "<img src='res/img/first_state_16_16.gif' alt=''>";
    var BLANK_IMAGE = "<img src='res/img/blank_16_16.gif' alt=''>";
    
    //Si en cualquier momento alguna validación no pasa esta bandera se vuelve false y
    //es usada por el evento submit de la forma para evitar el submit
    var validationPassed = true;
    
    isValidationPassed = function(){
        return validationPassed;
    }
    
    /**
     * Valida que cada una de las transiciones proporcionadas por el usuario en la tabla que 
     * representa al autómata finito
     */
    validateADFTableCells = function(){
        //Concatenamos los estados y los ponemos en el input de estados
        var statesString = "";
        $(".states").each(function() { 
            statesString += $(this).text() + ",";
        });
        var states = $("#statesInput").val().split(',');
        //Validamos todas las celdas asegurándonos que únicamente se usen estados como transiciones                       
        validationPassed = true;                        
        $(".adfTableCells").each(function(index,inputCell) { 
            var cellStates = $(inputCell).val().split(',');
            $(cellStates).each(function(index, state) { 
                if($.inArray(state,states) == -1){
                    //Si no ponen nada significa que en ese estado simplemente no hay transición,
                    if(state == ""){                                    
                        return true;
                    }
                    //Introdujeron un estado que no existe
                    $(inputCell).animate({
                        backgroundColor: "#FEC1BA"
                    }, 500);
                    validationPassed = false;
                }
            });
        });
    }
    
    /**
     * Indica como estado inicial el estado superior o el último si nos encontramos
     * en el primer estado de la tabla
     */
    pushUpInitialState = function(){
        var indexOfFirstState = getIndexOfActualFirstState();
        var indexOfNewFirstState = 0;
        if(indexOfFirstState != 0){
            indexOfNewFirstState = indexOfFirstState -1;
        }else{
            indexOfNewFirstState = $(".firstStateMarker").size() - 1;                            
        }
        stablishNewFirstState(indexOfNewFirstState);
    }
    
    /*
     * Baja un estado la marca de estado inicial. Si el último estado de la tabla es el estado
     * inicial envía el estado inicial al primer estado.
     */
    pushDownInitialState = function(){
        var indexOfFirstState = getIndexOfActualFirstState();
        var indexOfNewFirstState = 0;
        if(indexOfFirstState != $(".firstStateMarker").size() - 1){
            indexOfNewFirstState = indexOfFirstState +1;
        }
        stablishNewFirstState(indexOfNewFirstState);
    }
    
    /**
     * Itera sobre los estados y devuelve el índice del estado que actualmente es el
     * estado incial.
     */
    function getIndexOfActualFirstState(){
        var indexOfFirstState = 0;
        $(".firstStateMarker").each( function(index, element){
            if($(element).hasClass(IS_FIRST_STATE_CLASS)){
                indexOfFirstState = index;
                $(element).removeClass(IS_FIRST_STATE_CLASS);
                $(element).children().remove();
                $(element).append(BLANK_IMAGE);
                return false;
            }
        });
        return indexOfFirstState;
    }
                    
    /**
     * Establece el estado cuyo índice es indexOfNewFirstState y lo coloca como estado inicial.
     */
    function stablishNewFirstState(indexOfNewFirstState){
        var element = $($(".firstStateMarker")[indexOfNewFirstState]);
        $(element).children().remove();
        $(element).addClass(IS_FIRST_STATE_CLASS);
        $(element).append(FIRST_STATE_IMAGE);
    }
    
    /**
     * Esconde los componentes de captura de expresión regular y muestra los elementos de
     * captura de ADF
     */
    hideExpressionShowADF = function(immediate){
        var duration = 600;
        if(immediate){
            duration = 0;
        }
        $("#regularExpressionToADFContainer").hide(duration);
        $("#adfToRegularExpressionContainer").show(duration);
        $("#toggleOperation").val("Transformar Autómata Finito a Expresión Regular"); 
        $("#adfTableControls").hide(0);
        $("#adfTableResults").show(0);
    }
                    
    /**
     * Esconde los componentes de captura de ADF y muestra los componentes de captura de
     * expresión regular.
     */
    hideADFShowExpression = function(immediate){
        var duration = 600;
        if(immediate){
            duration = 0;
        }
        $("#regularExpressionToADFContainer").show(duration);
        $("#adfToRegularExpressionContainer").hide(duration);
        //Mostramos los controles para modificar estados de aceptación y estado inicial
        $("#toggleOperation").val("Transformar Expresión Regular a Autómata Finito");
    }
    
    /**
     * Decide que tipo de transformación debe mostrar, ADF a expresión regular o expresión regular
     * a ADF. Para tomar dicha desición busca en el controlador el semáforo lastConversion que se
     * modifica cada vez que realizamos una conversión.
     */
    hideOrShowComponents = function(){
        //Es importante notar que estoy obteniendo un elemento de JSF utilizando JQUERY por esa
        //razón no puedo usar directamente el id y tengo que concatenar el id de la forma. No me
        //gusta mezclar elementos html con elementos JSF por cuestiones de normalización pero
        //lo hago para procurar enviar la menor cantidad en el post de la forma.
        var lastConversion = $("#ConverterForm\\:lastConversion").val();
        if(lastConversion == 'expression'){
            hideADFShowExpression(true);
        }else{
            hideExpressionShowADF(true);
        }
    }
    
    /**
     * Lee el alfabeto proporcionado los estados proporcionados o en su defecto la tabla en formato String y crea una tabla
     * en pantalla que permite caprutar las transiciones del ADF.
     */
    createAutomataTable = function(){
        //Obtenemos el alfabeto y estados proporcionados por el usuario separando por comas.
        var alphabet = $("#alphabetInput").val().split(",");
        var states = $("#statesInput").val().split(',');
        //Eliminamos espacios en blanco y verificamos que hayan capturado algo antes de presionar el botón.
        if($.trim(alphabet) == "" || $.trim(states) == ""){
            alert("Debe proporcionar un alfabeto y por lo menos un estado para poder continuar.");
            return;
        }                        
        //Eliminamos cualquier alfabeto insertado en la tabla con anterioridad
        $("#adfInputTableAlphabet").children().remove();
        //Esta es la celda de la esquina que no tiene ningún contenido
        $("#adfInputTableAlphabet").append("<td></td>");
        //Ahora iteramos por el alfabeto y lo vamos incluyendo como título de las columnas de la tabla.
        $.each(alphabet, function(index, alphabetChar) { 
            $("#adfInputTableAlphabet").append("<td>"+alphabetChar+"</td>");
        });
        //Eliminamos cualquier estado creado con anterioridad
        $("#adfInputTableTransitions").children().remove();
        //Iteramos por los estados incluyendo el nombre del estado como título de la fila y añadiendo
        //las celdas en las que se insertarán las transiciones.
        $.each(states, function(stateIndex, state) { 
            //Creamos la fila con un id único que después se utilizará al enviar el formulario
            $("#adfInputTableTransitions").append("<tr id=row_"+state+"></tr>");
            var stateTemplate = "<td><table><tr><td class='firstStateMarker {3}'>{0}</td><td class='notAceptanceState' id='aceptance_state_label_{2}'>{1}</td><td id='state_label_{2}' class='states'>{2}</td></tr></table></td>";                                           
            $.each(alphabet, function(alphabetIndex, alphabetChar) {                                 
                //Si estamos en la primer columna entonces utilizamos el nombre del estado para etiquetar la fila
                if(alphabetIndex == 0){
                    //Si estamos en la primer columna y en la primer fila marcamos como estado inicial el estado             
                    if(stateIndex == 0){   
                        $("#row_"+state).append(String.format(stateTemplate,FIRST_STATE_IMAGE,BLANK_IMAGE, state, IS_FIRST_STATE_CLASS));
                    } else{
                        $("#row_"+state).append(String.format(stateTemplate,BLANK_IMAGE,BLANK_IMAGE, state, ""));
                    }       
                    bindAceptanceChangeEventToStates(state);
                }                                                                    
                //Comenzamos a pegar cada inputText para que el usuario pueda capturar las transiciones
                //Nótese que incluimos una clase a la celda para poder iterar sobre ellas despues.                                
                $("#row_"+state).append("<td><input size='10' class='adfTableCells' id='cell_"+state+"_"+alphabetChar+"' type='text' /></td>");  
                //Le añadirmos el evento onchange
                bindOnChangeToTransitionCell(state,alphabetChar)
            });
            $("#adfInputTableTransitions").append("</tr>");
        });
        //Mostramos los controles para modificar estados de aceptación y estado inicial
        $("#adfTableControls").show(0);
        $("#adfTableResults").hide(0);
    }
    
    function bindOnChangeToTransitionCell(state,alphabetChar){
        $("#cell_"+state+"_"+alphabetChar).bind("change",function(){
            //Si algo cambia que regrese a blanco por si antes no había pasado alguna validación.
            $(this).animate({
                backgroundColor: "#FFFFFF"
            }, 500);
        });
    }
    
    function bindAceptanceChangeEventToStates(state){
        //Añadimos un evento onclick a todos los estados para que al presionar sobre ellos se coloquen como aceptación o no aceptación
        $("#state_label_"+state).bind("click", function(event){
            var aceptanceStateId = "aceptance_" + $($(event).attr("target")).attr("id");  
            $("#"+aceptanceStateId).children().remove();
            if ( $("#"+aceptanceStateId).attr("class") == "notAceptanceState" ) {
                // Si no es de aceptación lo hacemos de aceptación
                $("#"+aceptanceStateId).removeClass("notAceptanceState");      
                $("#"+aceptanceStateId).addClass("aceptanceState");          
                $("#"+aceptanceStateId).append("<img src='res/img/aceptance_state_16_16.png' alt=''>");
            }else{
                // Si es de aceptación lo hacemos de no aceptación
                $("#"+aceptanceStateId).removeClass("aceptanceState");      
                $("#"+aceptanceStateId).addClass("notAceptanceState");        
                $("#"+aceptanceStateId).append(BLANK_IMAGE);
            }
        });
    }
    
    /**
     * Función que toma un string y reemplaza en los lugares que encuentra {N} con
     * sus respectivos argumentos.
     */
    String.format = function() {
        var s = arguments[0];
        for (var i = 0; i < arguments.length - 1; i++) {       
            var reg = new RegExp("\\{" + i + "\\}", "gm");             
            s = s.replace(reg, arguments[i + 1]);
        }
        return s;
    }  
})(jQuery);